package i18n

import (
	"errors"
	"fmt"
	"slices"
	"sort"
	"strings"
	"sync"

	"golang.org/x/text/language"
)

// Bundle 多语言包管理器
type Bundle struct {
	mu             *sync.RWMutex
	messages       map[string]*langMessages // map[langName] FlatMapMessages
	localizes      map[string]Localizer     // map[langName]Localizer
	supportedLangs *supportedLangs          // 受支持的语言管理器
	isDefault      bool                     // 是否为默认语言包管理器
}
type langMessages struct {
	langName string
	langTag  language.Tag
	data     FlatMapMessages
	mu       *sync.RWMutex
}
type supportedLangs struct {
	mu          *sync.RWMutex
	uniqueIndex map[string]language.Tag // 语言名称和标签的映射，便于查找语言名称对应的标签及优先级排序
	tags        []language.Tag
	names       []string
	matcher     language.Matcher
	defaultName string       // 默认语言名称(回退机制)
	defaultTag  language.Tag // 默认语言标签(回退机制)
}

// NewBundle 创建新的国际化资源包
func NewBundle() *Bundle {
	b := &Bundle{
		mu:             new(sync.RWMutex),
		messages:       make(map[string]*langMessages),
		localizes:      make(map[string]Localizer),
		supportedLangs: newSupportedLangs(),
	}
	return b
}

// GetOneMessage 获取指定语言名称和语言Key的文本消息
//   - 如果不存在则返回nil和false
func GetOneMessage(langName, key string) (*Message, bool) {
	return GetDefaultBundle().GetOneMessage(langName, key)
}

// GetOneMessage 获取指定语言名称和语言Key的文本消息
//   - 如果不存在则返回nil和false
func (b *Bundle) GetOneMessage(langName, key string) (*Message, bool) {
	parseLangName, err := ParseLangName(langName)
	if err != nil {
		return nil, false
	}
	parseKey, err := ParseKey(key)
	if err != nil {
		return nil, false
	}
	return b.getOneMessage(parseLangName, parseKey)
}

// getOneMessage 获取指定语言名称和语言Key的消息
//   - 此函数供内部调用，langName, key 已通过有效性校验, 无需再次校验
//   - 如果不存在则返回nil和false
func (b *Bundle) getOneMessage(langName, key string) (*Message, bool) {
	b.mu.RLock()
	messages, exists := b.messages[langName]
	b.mu.RUnlock()
	if !exists || messages == nil {
		return nil, false
	}
	return messages.GetOneMessage(key)
}

// GetMessages 获取指定语言名称的所有消息
//   - 返回 FlatMapMessages 扁平化消息数据(一维平铺的的消息数据))
//   - 返回的是复制数据，防止外部修改影响内部数据
//   - 如果指定语言不存在则返回nil
func GetMessages(langName string) FlatMapMessages {
	return GetDefaultBundle().GetMessages(langName)
}

// GetMessages 获取指定语言名称的所有消息
//   - 返回 FlatMapMessages 扁平化消息数据(一维平铺的的消息数据))
//   - 返回的是复制数据，防止外部修改影响内部数据
//   - 如果指定语言不存在则返回nil
func (b *Bundle) GetMessages(langName string) FlatMapMessages {
	b.mu.RLock()
	messages, exists := b.messages[langName]
	b.mu.RUnlock()
	if !exists || messages == nil {
		return nil
	}
	return messages.GetMessages()
}

// SetOneMessage 设置单个消息，如果键不存在则创建新条目，如果已存在，则合并
func SetOneMessage(langName, key string, msg *Message) error {
	return GetDefaultBundle().SetOneMessage(langName, key, msg)
}

// SetOneMessage 设置单个消息，如果键不存在则创建新条目，如果已存在，则合并
func (b *Bundle) SetOneMessage(langName string, key string, msg *Message) error {
	if err := b.SetSupportedLangs(langName); err != nil {
		return fmt.Errorf("SetOneMessage: langName[%s] error, %s", langName, err.Error())
	}
	lang, err := ParseLangName(langName)
	if err != nil {
		return fmt.Errorf("SetOneMessage: langName[%s] error, %s", lang, err.Error())
	}
	parseKey, err := ParseKey(key)
	if err != nil {
		return fmt.Errorf("SetOneMessage: key[%s] error, %s", key, err.Error())
	}
	if err := ParseMessage(msg); err != nil {
		return fmt.Errorf("SetOneMessage: key[%s] error, %s", key, err.Error())
	}
	b.mu.Lock()
	err = b.messages[lang].SetOneMessage(parseKey, msg)
	b.mu.Unlock()
	return err
}

// SetMessages 设置一组消息，如果键不存在则创建新条目，如果已存在，则合并
func SetMessages(langName string, data FlatMapMessages) error {
	return GetDefaultBundle().SetMessages(langName, data)
}

// SetMessages 设置一组消息，如果键不存在则创建新条目，如果已存在，则合并
func (b *Bundle) SetMessages(langName string, data FlatMapMessages) error {
	if err := b.SetSupportedLangs(langName); err != nil {
		return fmt.Errorf("SetMessages: langName[%s] error, %s", langName, err.Error())
	}
	lang, err := ParseLangName(langName)
	if err != nil {
		return fmt.Errorf("SetMessages: langName[%s] error, %s", langName, err.Error())
	}
	if data == nil {
		return errors.New("SetMessages: data[Messages] is nil")
	}
	if len(data) == 0 {
		return nil // 不处理空数据
	}
	for k, msg := range data {
		key, err := ParseKey(k)
		if err != nil {
			return fmt.Errorf("SetMessages: key[%s] error, %s", k, err.Error())
		}
		if err := ParseMessage(msg); err != nil {
			return fmt.Errorf("SetMessages: key[%s] error, %s", key, err.Error())
		}
		b.mu.Lock()
		err = b.messages[lang].SetOneMessage(key, msg)
		b.mu.Unlock()
		if err != nil {
			return err
		}
	}
	return nil
}

// HasDefaultLang 检查是否存在默认语言设置
func HasDefaultLang() bool {
	return GetDefaultBundle().HasDefaultLang()
}

// HasDefaultLang 检查是否存在默认语言设置
func (b *Bundle) HasDefaultLang() bool {
	return b.supportedLangs.HasDefault()
}

// GetDefaultLang 获取默认语言，例如：zh-CN, en-US 等简短格式的语言标签
func GetDefaultLang() string {
	return GetDefaultBundle().GetDefaultLang()
}

// GetDefaultLang 获取默认语言，例如：zh-CN, en-US 等简短格式的语言标签
func (b *Bundle) GetDefaultLang() string {
	return b.supportedLangs.GetDefaultName()
}

// SetDefaultLang 设置默认语言，例如：zh-CN, en-US 等简短格式的语言标签
func SetDefaultLang(lang string) error {
	return GetDefaultBundle().SetDefaultLang(lang)
}

// SetDefaultLang 设置默认语言，例如：zh-CN, en-US 等简短格式的语言标签
func (b *Bundle) SetDefaultLang(langName string) error {
	tag, err := ParseLangTag(langName)
	if err != nil {
		return err
	}
	b.supportedLangs.SetDefaultTag(tag)
	b.supportedLangs.UpdateMatcher()
	// 检查语言包是否存在，否则自动初始化
	langName = tag.String()
	b.mu.Lock()
	if _, exists := b.messages[langName]; !exists {
		b.messages[langName] = newLangMessages(langName, tag)
		b.localizes[langName] = &localize{bundle: b, langName: langName}
	}
	b.mu.Unlock()
	if b.isDefault {
		loc := NewLocalizer(langName)
		defaultLocalizer.Store(loc)
	}
	return nil
}

// GetSupportedLangs 获取支持的语言列表，例如：zh-CN, en-US 等简短格式的语言标签
func GetSupportedLangs() []string {
	return GetDefaultBundle().GetSupportedLangs()
}

// GetSupportedLangs 获取受支持的语言列表，例如：zh-CN, en-US 等简短格式的语言标签
func (b *Bundle) GetSupportedLangs() []string {
	return b.supportedLangs.GetNames()
}

// SetSupportedLangs 设置支持的语言列表，例如：zh-CN, en-US 等简短格式的语言标签
func SetSupportedLangs(supportedLangs ...string) error {
	return GetDefaultBundle().SetSupportedLangs(supportedLangs...)
}

// SetSupportedLangs 设置受支持的语言列表，例如：zh-CN, en-US 等简短格式的语言标签
func (b *Bundle) SetSupportedLangs(langNames ...string) error {
	if len(langNames) == 0 {
		return nil
	}
	isPushed := false
	for _, langName := range langNames {
		tag, err := ParseLangTag(langName)
		if err != nil {
			return err
		}
		if b.supportedLangs.HasTag(tag) {
			continue
		}
		if b.supportedLangs.Push(tag) {
			isPushed = true
		}
		// 检查语言包是否存在，否则自动初始化
		langName = tag.String()
		b.mu.Lock()
		if _, ok := b.messages[langName]; !ok {
			b.messages[langName] = newLangMessages(langName, tag)
			b.localizes[langName] = &localize{bundle: b, langName: langName}
		}
		b.mu.Unlock()
	}
	if isPushed {
		b.supportedLangs.UpdateMatcher()
	}
	return nil
}

// HasSupportedLangName 检查指定的语言名称是否受支持，例如：zh-CN, en-US 等简短格式的语言标签
func HasSupportedLangName(lang string) bool {
	return GetDefaultBundle().HasSupportedLangName(lang)
}

// HasSupportedLangName 检查指定的语言名称是否受支持，例如：zh-CN, en-US 等简短格式的语言标签
func (b *Bundle) HasSupportedLangName(langName string) bool {
	name, err := ParseLangName(langName)
	if err != nil {
		return false
	}
	return b.supportedLangs.HasName(name)
}

// HasSupportedLangTag 检查指定的语言标签是否受支持，例如：language.Eanglish | language.SimplifiedChinese 等语言标签
func HasSupportedLangTag(tag language.Tag) bool {
	return GetDefaultBundle().HasSupportedLangTag(tag)
}

// HasSupportedLangTag 检查指定的语言标签是否受支持，例如：language.Eanglish | language.SimplifiedChinese 等语言标签
func (b *Bundle) HasSupportedLangTag(tag language.Tag) bool {
	return b.supportedLangs.HasTag(tag)
}

// newLangMessages 初始化一种语言管理器
// 此函数供 Bundle 使用，参数由 Bundle 完成校验和传入
//   - langName  语言名称，如 "zh-CN"
//   - langTag   语言标签，language.Tag
func newLangMessages(langName string, langTag language.Tag) *langMessages {
	return &langMessages{
		langName: langName,
		langTag:  langTag,
		data:     make(map[string]*Message),
		mu:       new(sync.RWMutex),
	}
}

// GetOneMessage 获取一条消息，如果不存在则返回 nil, false
func (lm *langMessages) GetOneMessage(key string) (*Message, bool) {
	lm.mu.RLock()
	defer lm.mu.RUnlock()
	v, ok := lm.data[key]
	if !ok || v == nil {
		return nil, false
	}
	return v, true
}

// GetMessages 获取当前语言 FlatMapMessages 扁平化消息数据(一维平铺的的消息数据)
//   - 返回的是复制数据，防止外部修改影响内部数据
func (lm *langMessages) GetMessages() FlatMapMessages {
	lm.mu.RLock()
	defer lm.mu.RUnlock()
	data := make(FlatMapMessages, len(lm.data))
	for k, v := range lm.data {
		data[k] = v.Clone()
	}
	return data
}

// SetMessages 设置一组消息，如果键不存在则创建新条目，如果已存在，则合并。
//   - 注意: data 已通过有效性校验, 无需再次校验
func (lm *langMessages) SetMessages(data FlatMapMessages) error {
	lm.mu.Lock()
	defer lm.mu.Unlock()
	for k, v := range data {
		key, err := ParseKey(k)
		if err != nil {
			return fmt.Errorf("SetMessages: key[%s], %s", k, err.Error())
		}
		if err := ParseMessage(v); err != nil {
			return fmt.Errorf("SetMessages: key[%s], %s", key, err.Error())
		}
		if err := lm.setOneMessage(key, v); err != nil {
			return err
		}
	}
	return nil
}

// SetOneMessage 设置单个消息，如果键不存在则创建新条目，如果已存在，则合并。
//   - 注意: key, data 已通过有效性校验, 无需再次校验
func (lm *langMessages) SetOneMessage(key string, msg *Message) error {
	lm.mu.Lock()
	defer lm.mu.Unlock()
	return lm.setOneMessage(key, msg)
}

// setOneMessage 设置单个消息，如果键不存在则创建新条目，如果已存在，则合并
//   - 注意: key, data 已通过有效性校验, 无需再次校验, 由上级入口函数保证锁安全
func (lm *langMessages) setOneMessage(key string, msg *Message) (err error) {
	// 不存在, 直接插入当前 key/Message 到 curMap
	if _, ok := lm.data[key]; !ok {
		lm.data[key] = msg
		return nil
	}
	// 存在, 合并Key相同的两条消息
	return lm.mergeMessageFrom(msg, lm.data[key])
}

// mergeMessageFrom 合并Key相同的两条消息, 将dst消息合并到target目标中，如果存在冲突则优先保留dst中的值。
//   - 注意: dst, target 已通过有效性校验, 无需再次校验, 由上级入口函数保证锁安全
func (lm *langMessages) mergeMessageFrom(dst, target *Message) error {
	// 不能同时设置值，简单消息和复数消息只能使用其中一项
	if dst.Text != "" && dst.Plural != nil {
		return fmt.Errorf("mergeMessageFrom: Check dst.Text And dst.Plural, `Text` and `Plural` message can only be used for one of the following")
	}
	// 简单消息和复数消息只能使用其中一项, 两者都设置了，则优先保留dst中的值
	if dst.Text != "" {
		target.Text = dst.Text
		target.Plural = nil // 清除复数消息，防止后续误用
	} else if dst.Plural != nil {
		target.Text = "" // 清除简单消息，防止后续误用
		target.Plural = dst.Plural
	}
	return nil
}

// newSupportedLangs 创建支持语言清单管理器
func newSupportedLangs() *supportedLangs {
	return &supportedLangs{
		mu:          new(sync.RWMutex),
		uniqueIndex: make(map[string]language.Tag),
		tags:        make([]language.Tag, 0, 3),
		names:       make([]string, 0, 3),
	}
}

// Size 获取当前支持的语种数量
func (s *supportedLangs) Size() int {
	s.mu.RLock()
	c := len(s.tags)
	s.mu.RUnlock()
	return c
}

// HasTag 检查是否存在此语言标签
//   - 注意: 这里不校验 tag 参数, 由上级入口函数保证参数有效性校验已完成
//   - 传入的语言标签必须是标准语言标签Tag, 例如: language.Chinese 等
func (s *supportedLangs) HasTag(tag language.Tag) bool {
	if tag == language.Und {
		return false
	}
	s.mu.RLock()
	_, ok := s.uniqueIndex[tag.String()]
	s.mu.RUnlock()
	return ok
}

// HasName 检查是否存在此语言名称
//   - 注意: 这里不校验 name 参数, 由上级入口函数保证参数有效性校验已完成
//   - 传入的语言名称必须是标准语言标签名称, 例如: "zh-CN" 等
func (s *supportedLangs) HasName(name string) bool {
	if name == "" {
		return false
	}
	s.mu.RLock()
	_, ok := s.uniqueIndex[name]
	s.mu.RUnlock()
	return ok
}

// Push 添加一个语言标签到支持列表中, 如果已存在则忽略不处理
func (s *supportedLangs) Push(tag language.Tag) bool {
	if tag == language.Und {
		return false
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.uniqueIndex[tag.String()]; ok {
		return false
	}
	return s._push(tag)
}
func (s *supportedLangs) _push(tag language.Tag) bool {
	s.uniqueIndex[tag.String()] = tag
	// 排序 s.names
	s.names = append(s.names, tag.String())
	sort.Strings(s.names)
	// 重建 s.tags , 确保 defaultTag 总是排在第一位，便于后续处理
	s.tags = s.tags[:0]
	if s.defaultName != "" {
		s.tags = append(s.tags, s.defaultTag)
	}
	for _, name := range s.names {
		if name != s.defaultName {
			s.tags = append(s.tags, s.uniqueIndex[name])
		}
	}
	return true
}

// SetDefaultTag 设置默认语言标签
func (s *supportedLangs) SetDefaultTag(tag language.Tag) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.defaultTag == tag || tag == language.Und {
		return false
	}
	s.defaultTag = tag
	s.defaultName = tag.String()
	s._push(tag)
	return true
}

// HasDefault 检查是否存在默认语言
func (s *supportedLangs) HasDefault() bool {
	s.mu.RLock()
	has := s.defaultName != "" && s.HasName(s.defaultName)
	s.mu.RUnlock()
	return has
}

// GetDefaultName 获取默认语言名称
func (s *supportedLangs) GetDefaultName() string {
	s.mu.RLock()
	name := s.defaultName
	s.mu.RUnlock()
	return name
}

// GetDefaultTag 获取默认语言标签
func (s *supportedLangs) GetDefaultTag() language.Tag {
	s.mu.RLock()
	tag := s.defaultTag
	s.mu.RUnlock()
	return tag
}

// UpdateMatcher 更新匹配器，用于后续最佳匹配语言标签操作
//   - 注意: s.tags 缓存的标签列表未更新，无需再次更新匹配器
func (s *supportedLangs) UpdateMatcher() bool {
	s.mu.Lock()
	s.matcher = language.NewMatcher(s.tags)
	s.mu.Unlock()
	return true
}
func (s *supportedLangs) _updateMatcher() bool {
	s.matcher = language.NewMatcher(s.tags)
	return true
}

// MatchTags 通过一组语言标签进行最佳匹配
//   - 返回最佳匹配的语言标签和是否成功标记
func (s *supportedLangs) MatchTags(acceptedTags ...language.Tag) (language.Tag, bool) {
	// 首先, 尝试精确匹配语言标签
	for _, tag := range acceptedTags {
		if s.HasTag(tag) {
			return tag, true
		}
	}
	// 使用匹配器进行最佳匹配
	if s.matcher == nil {
		s.mu.Lock()
		s._updateMatcher()
		s.mu.Unlock()
	}
	s.mu.RLock()
	defer s.mu.RUnlock()
	tag, index, confidence := s.matcher.Match(acceptedTags...)
	if index < 0 || tag == language.Und || confidence == language.No {
		return language.Und, false
	}
	if index < len(s.tags) {
		return s.tags[index], true
	}
	return language.Und, false
}

// MatchStrings 通过一组语言名称字符串进行最佳匹配
//   - 返回最佳匹配的语言标签和是否成功标记
func (s *supportedLangs) MatchStrings(acceptedLangs ...string) (string, bool) {
	// 解析用户接受的语言
	// 首先, 尝试精确匹配语言名称，例如：zh zh-CN zh-Hans en en-US 等简短格式的语言标签
	for _, lang := range acceptedLangs {
		if !strings.Contains(lang, ";") {
			if name, err := ParseLangName(lang); err == nil && s.HasName(name) {
				return name, true
			}
		}
	}
	// 使用匹配器进行最佳匹配
	// 支持最佳匹配来自 HTTP Accept-Language 头部格式的语言标签列表，例如：
	// zh;q=0.8,en;q=0.5,en-US;q=0.3
	// zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
	if s.matcher == nil {
		s.mu.Lock()
		s._updateMatcher()
		s.mu.Unlock()
	}
	s.mu.RLock()
	defer s.mu.RUnlock()
	tag, index := language.MatchStrings(s.matcher, acceptedLangs...)
	if index < 0 || tag == language.Und {
		return "", false
	}
	if index < len(s.tags) {
		return s.tags[index].String(), true
	}
	return "", false
}
func (s *supportedLangs) GetTags() []language.Tag {
	s.mu.RLock()
	cp := slices.Clone(s.tags)
	s.mu.RUnlock()
	return cp
}
func (s *supportedLangs) GetNames() []string {
	s.mu.RLock()
	cp := slices.Clone(s.names)
	s.mu.RUnlock()
	return cp
}
